package com.hjy.demo.config;

import com.hjy.demo.pojo.User;
import com.hjy.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 自定义的认证和授权域
 */
public class MyShiroRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;
    /*主要是用来进行身份认证的，也就是说验证用户输入的账号和密码是否正确。*/
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //用户登录次数计数  redisKey 前缀
    private String SHIRO_LOGIN_COUNT = "shiro_login_count_";
    //用户登录是否被锁定    一小时 redisKey 前缀
    private String SHIRO_IS_LOCK = "shiro_is_lock_";

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("权限认证-->MyShiroRealm.doGetAuthorizationInfo()");
        //该Shiro工具类通过ThreadLocal取出当前请求线程的Subject（主体）
        //
        Subject subject= SecurityUtils.getSubject();
        //从主体中取得User对象
        User user = (User) subject.getPrincipal();

        User u = userService.getUser(user.getUsrId());

         //创建一个简单授权信息对象
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //封装来自数据库user表perms字段的权限值
        info.addStringPermission(u.getUsrPerms());
//        info.addStringPermission("用户添加");
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        System.out.println("身份认证：MyShiroRealm.doGetAuthenticationInfo()");


        //获取用户的输入的账号.
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String usrName = token.getUsername();
        String usrPassword = new String(token.getPassword());
        System.out.println("usrName:" + token.getUsername());
        System.out.println("usrPassword:" + token.getPassword());

        //访问一次，计数一次
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        opsForValue.increment(SHIRO_LOGIN_COUNT+usrName, 1);
        //计数大于 5 时，设置用户被锁定一小时
        if(Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT+usrName))>5){
            opsForValue.set(SHIRO_IS_LOCK+usrName, "LOCK");
            stringRedisTemplate.expire(SHIRO_IS_LOCK+usrName, 1, TimeUnit.HOURS);
            //清空登录计数
            opsForValue.set(SHIRO_LOGIN_COUNT + usrName, "0");
        }
        if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK+usrName))){

            throw new DisabledAccountException("由于输入错误次数大于 5 次，帐号 1 小时内已经禁 止登录！");
        }

        //通过 username 从数据库中查找 User 对象，如果找到，没找到.
        // 实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro 自己也是有时间间隔机 制，2 分钟内不会重复执行该方法
        User user = userService.findUserByName(usrName);
        System.out.println("----->>user=" + user);
        if (user == null) {
            throw new UnknownAccountException("账号不存在！");
        }
//        else if (!user.getUsrPassword().equals(usrPassword)) {
//            throw new IncorrectCredentialsException("密码不正确！");
//        }
        //认证信息
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user, //用户
                user.getUsrPassword(), //密码
                ByteSource.Util.bytes("salt"),
                this.getName()  //realm name
        );
        return authenticationInfo;
    }
}
